# -*- coding: utf-8 -*-
# Author   : ZhangQing
# Time     : 2025-07-15 23:21
# File     : market_data_service.py
# Project  : dynamic-portfolio-optimizer
# Desc     : 主要服务类

from typing import Dict, List, Optional, Any, Union
import pandas as pd
from datetime import datetime, timedelta
from ..data.aggregator import DataAggregator, DataRequest
from ..data.cache_manager import CacheManager
import logging

logger = logging.getLogger(__name__)


class MarketDataService:
    """金融市场数据服务"""

    def __init__(self, enable_cache: bool = True):
        self.aggregator = DataAggregator()
        self.cache_manager = CacheManager() if enable_cache else None

        # 预定义的数据源优先级
        self.source_priorities = {
            'real_time': ['alpaca', 'polygon', 'finnhub', 'ib_api'],
            'historical': ['yfinance', 'alpha_vantage', 'twelve_data', 'polygon'],
            'fundamentals': ['alpha_vantage', 'finnhub', 'twelve_data', 'yfinance'],
            'options': ['yfinance', 'twelve_data', 'ib_api'],
            'news': ['finnhub', 'alpha_vantage', 'polygon', 'yfinance']
        }

    def get_stock_price(self, symbols: Union[str, List[str]],
                        start_date: Optional[datetime] = None,
                        end_date: Optional[datetime] = None,
                        interval: str = '1d',
                        sources: Optional[List[str]] = None) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]:
        """
        获取股票价格数据

        Args:
            symbols: 股票代码或代码列表
            start_date: 开始日期
            end_date: 结束日期
            interval: 时间间隔 ('1m', '5m', '1h', '1d')
            sources: 指定数据源

        Returns:
            单个股票返回DataFrame，多个股票返回字典
        """
        # 默认时间范围
        if end_date is None:
            end_date = datetime.now()
        if start_date is None:
            start_date = end_date - timedelta(days=365)

        # 确定优先级数据源
        priority_sources = sources or self.source_priorities['historical']

        # 处理单个股票
        if isinstance(symbols, str):
            return self._get_single_stock_data(
                symbols, start_date, end_date, interval, priority_sources
            )

        # 处理多个股票
        results = {}
        for symbol in symbols:
            try:
                data = self._get_single_stock_data(
                    symbol, start_date, end_date, interval, priority_sources
                )
                if not data.empty:
                    results[symbol] = data
                    logger.info(f"✅ 成功获取{symbol}数据")
                else:
                    logger.warning(f"⚠️ 未获取到{symbol}数据")
            except Exception as e:
                logger.error(f"❌ 获取{symbol}数据失败: {e}")

        return results

    def _get_single_stock_data(self, symbol: str, start_date: datetime,
                               end_date: datetime, interval: str,
                               priority_sources: List[str]) -> pd.DataFrame:
        """获取单个股票数据"""
        # 检查缓存
        if self.cache_manager:
            cached_data = self.cache_manager.get(
                symbol, start_date, end_date, interval, 'stock'
            )
            if cached_data is not None:
                return cached_data

        # 创建数据请求
        request = DataRequest(
            symbol=symbol,
            start_date=start_date,
            end_date=end_date,
            interval=interval,
            data_type='stock',
            priority_sources=priority_sources
        )

        # 获取数据
        data = self.aggregator.get_stock_data(request)

        # 缓存数据
        if self.cache_manager and not data.empty:
            cache_hours = 24 if interval == '1d' else 1  # 日线数据缓存24小时，分钟数据缓存1小时
            self.cache_manager.set(
                symbol, start_date, end_date, interval, 'stock', data, cache_hours
            )

        return data

    def get_options_chain(self, symbol: str, expiration_date: datetime,
                          sources: Optional[List[str]] = None) -> pd.DataFrame:
        """
        获取期权链数据

        Args:
            symbol: 股票代码
            expiration_date: 到期日
            sources: 指定数据源

        Returns:
            期权链DataFrame
        """
        # 检查缓存
        if self.cache_manager:
            cached_data = self.cache_manager.get(
                symbol, expiration_date, expiration_date, '1d', 'options'
            )
            if cached_data is not None:
                return cached_data

        priority_sources = sources or self.source_priorities['options']

        request = DataRequest(
            symbol=symbol,
            start_date=expiration_date,
            end_date=expiration_date,
            data_type='options',
            priority_sources=priority_sources
        )

        data = self.aggregator.get_options_data(request)

        # 缓存期权数据
        if self.cache_manager and not data.empty:
            self.cache_manager.set(
                symbol, expiration_date, expiration_date, '1d', 'options', data, 6
            )

        return data

    def get_company_fundamentals(self, symbols: Union[str, List[str]],
                                 sources: Optional[List[str]] = None) -> Union[
        Dict[str, Any], Dict[str, Dict[str, Any]]]:
        """
        获取公司基本面数据

        Args:
            symbols: 股票代码或代码列表
            sources: 指定数据源

        Returns:
            基本面数据字典
        """
        priority_sources = sources or self.source_priorities['fundamentals']

        if isinstance(symbols, str):
            return self._get_single_fundamentals(symbols, priority_sources)

        results = {}
        for symbol in symbols:
            try:
                data = self._get_single_fundamentals(symbol, priority_sources)
                if data:
                    results[symbol] = data
                    logger.info(f"✅ 成功获取{symbol}基本面数据")
            except Exception as e:
                logger.error(f"❌ 获取{symbol}基本面数据失败: {e}")

        return results

    def _get_single_fundamentals(self, symbol: str, priority_sources: List[str]) -> Dict[str, Any]:
        """获取单个股票基本面数据"""
        # 检查缓存
        if self.cache_manager:
            cached_data = self.cache_manager.get(
                symbol, datetime.now(), datetime.now(), '1d', 'fundamentals'
            )
            if cached_data is not None:
                return cached_data

        request = DataRequest(
            symbol=symbol,
            start_date=datetime.now(),
            end_date=datetime.now(),
            data_type='fundamentals',
            priority_sources=priority_sources
        )

        data = self.aggregator.get_fundamentals(request)

        # 缓存基本面数据（24小时）
        if self.cache_manager and data:
            self.cache_manager.set(
                symbol, datetime.now(), datetime.now(), '1d', 'fundamentals', data, 24
            )

        return data

    def get_market_news(self, symbols: Union[str, List[str]],
                        limit: int = 20,
                        sources: Optional[List[str]] = None) -> List[Dict[str, Any]]:
        """
        获取市场新闻

        Args:
            symbols: 股票代码或代码列表
            limit: 新闻数量限制
            sources: 指定数据源

        Returns:
            新闻列表
        """
        priority_sources = sources or self.source_priorities['news']
        all_news = []

        symbol_list = [symbols] if isinstance(symbols, str) else symbols

        for symbol in symbol_list:
            try:
                request = DataRequest(
                    symbol=symbol,
                    start_date=datetime.now(),
                    end_date=datetime.now(),
                    data_type='news',
                    priority_sources=priority_sources
                )

                news = self.aggregator.get_news(request, limit // len(symbol_list))
                all_news.extend(news)

            except Exception as e:
                logger.error(f"❌ 获取{symbol}新闻失败: {e}")

        # 去重并排序
        unique_news = {}
        for news in all_news:
            title = news.get('title', '').lower().strip()
            if title and title not in unique_news:
                unique_news[title] = news

        sorted_news = sorted(
            unique_news.values(),
            key=lambda x: x.get('published_at', datetime.min),
            reverse=True
        )

        return sorted_news[:limit]

    def get_real_time_quote(self, symbols: Union[str, List[str]],
                            sources: Optional[List[str]] = None) -> Union[Dict[str, Any], Dict[str, Dict[str, Any]]]:
        """
        获取实时报价

        Args:
            symbols: 股票代码或代码列表
            sources: 指定数据源

        Returns:
            实时报价数据
        """
        priority_sources = sources or self.source_priorities['real_time']

        # 获取最新的1天数据作为实时报价
        end_date = datetime.now()
        start_date = end_date - timedelta(days=1)

        if isinstance(symbols, str):
            data = self._get_single_stock_data(
                symbols, start_date, end_date, '1m', priority_sources
            )

            if not data.empty:
                latest = data.iloc[-1]
                return {
                    'symbol': symbols,
                    'price': latest['close'],
                    'change': latest['close'] - latest['open'],
                    'change_percent': ((latest['close'] - latest['open']) / latest['open']) * 100,
                    'volume': latest['volume'],
                    'timestamp': latest.name,
                    'source': latest.get('source', 'unknown')
                }
            return {}

        results = {}
        for symbol in symbols:
            try:
                quote = self.get_real_time_quote(symbol, sources)
                if quote:
                    results[symbol] = quote
            except Exception as e:
                logger.error(f"❌ 获取{symbol}实时报价失败: {e}")

        return results

    def get_available_sources(self) -> List[str]:
        """获取可用数据源列表"""
        return self.aggregator.get_available_sources()

    def test_data_sources(self) -> Dict[str, bool]:
        """测试数据源连接状态"""
        return self.aggregator.test_connections()

    def clear_cache(self):
        """清理缓存"""
        if self.cache_manager:
            self.cache_manager.clear_cache()
            logger.info("🗑️ 缓存已清理")
